iBetter Books
수정

PART 05의 마무리는 지금까지 배운 조각을 하나로 엮는 실습입니다. 등록부(2장)와 인식(1장)을 합쳐, 웹캠에 잡힌 사람을 알아보고 이름과 함께 출석을 기록하는 프로토타입을 만듭니다. 강의실 자동 출석, 사무실 출입 기록의 가장 단순한 형태입니다.

무엇을 만드는가

동작은 이렇습니다.

  • 미리 만든 얼굴 DB(이름·인코딩)를 불러온다.
  • 웹캠 프레임마다 얼굴을 찾아 인코딩을 뽑고, DB와 비교해 이름을 정한다.
  • 처음 인식된 사람은 출석부에 시간과 함께 한 번만 기록한다.
  • 화면에는 박스와 이름을 실시간으로 표시한다.
flowchart TB A[얼굴 DB 로드] --> B[웹캠 프레임] B --> C[얼굴 검출·인코딩] C --> D[DB와 비교 → 이름] D --> E{처음 본 사람?} E -->|예| F[출석부에 시간 기록] E -->|아니오| G[표시만]

전체 코드

앞서 build_db.py로 만든 face_db.pkl이 있다고 가정합니다. 다음 내용을 attendance.py로 저장합니다.

# 파일: attendance.py"""얼굴 DB로 웹캠 출석을 기록한다. q 키로 종료."""import pickleimport csvfrom datetime import datetimeimport cv2import numpy as npimport face_recognitionwith open("face_db.pkl", "rb") as f:    db = pickle.load(f)known_encodings, known_names = db["encodings"], db["names"]recorded = set()                      # 이미 출석한 사람(중복 방지)def mark_attendance(name):    if name in recorded or name == "미등록자":        return    recorded.add(name)    with open("attendance.csv", "a", newline="", encoding="utf-8") as f:        csv.writer(f).writerow([name, datetime.now().strftime("%Y-%m-%d %H:%M:%S")])    print(f"출석 기록: {name}")cap = cv2.VideoCapture(0)while True:    ok, frame = cap.read()    if not ok:        break    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)         # face_recognition은 RGB    locations = face_recognition.face_locations(rgb)    encodings = face_recognition.face_encodings(rgb, locations)    for (top, right, bottom, left), enc in zip(locations, encodings):        distances = face_recognition.face_distance(known_encodings, enc)        name = "미등록자"        if len(distances) > 0:            best = np.argmin(distances)            if distances[best] < 0.6:                name = known_names[best]        mark_attendance(name)        color = (0, 255, 0) if name != "미등록자" else (0, 0, 255)        cv2.rectangle(frame, (left, top), (right, bottom), color, 2)        cv2.putText(frame, name, (left, top - 8),                    cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, cv2.LINE_AA)    cv2.imshow("attendance - press q to quit", frame)    if cv2.waitKey(1) & 0xFF == ord("q"):        breakcap.release()cv2.destroyAllWindows()

face_locations로 얼굴 위치를 먼저 구하고, 그 위치를 face_encodings에 함께 넘겨 인코딩을 뽑습니다(위치를 주면 다시 검출하지 않아 빠릅니다). DB와 거리를 비교해 이름을 정하고, mark_attendance가 처음 본 사람만 attendance.csv에 시간과 함께 기록합니다. recorded 집합으로 같은 사람을 매 프레임 중복 기록하는 것을 막습니다.

더 견고하게 만들기

이 프로토타입을 실제로 쓰려면 몇 가지를 보강합니다.

  • 속도: 매 프레임 인식하면 무겁습니다. 몇 프레임에 한 번만 인식하거나, 프레임을 줄여 처리하면 부드러워집니다.
  • 오인 방지: 한 프레임만 보고 기록하지 말고, 같은 이름이 여러 프레임 연속 잡혔을 때만 출석 처리하면 순간적인 오인을 거를 수 있습니다.
  • 위조 방지: 사진을 들이대는 부정 출석을 막으려면 살아 있는 얼굴인지 확인하는 절차가 필요합니다. 이는 PART 09(안티스푸핑)에서 다룹니다.

실무 팁. 출석·출입 기록은 개인정보입니다. 얼굴 인코딩과 기록을 저장할 때는 목적·보관 기간·접근 권한을 분명히 하고, 가능하면 원본 사진 대신 인코딩만 보관하는 편이 안전합니다. 기술 구현만큼이나 이 데이터를 어떻게 다룰지가 중요하며, 관련 윤리·법적 고려는 PART 11에서 따로 다룹니다.

이 장에서 기억할 것

출석 체크 프로토타입은 "DB 로드 → 프레임마다 인식 → 처음 본 사람만 시간 기록"의 흐름으로, 1~2장의 인코딩·DB·비교를 그대로 엮어 만듭니다. face_locationsface_encodings에 넘겨 속도를 높이고, recorded 집합으로 중복을 막습니다. 실전화에는 속도·연속 확인·위조 방지·개인정보 보호가 더해져야 합니다. 이로써 PART 05가 끝납니다. 다음 PART 06에서는 인식에 더해 감정·나이·성별까지 한 번에 처리하는 올인원 도구 DeepFace를 만납니다.